package com.rtsapp.server.domain.support;

import com.rtsapp.server.domain.support.jpa.ColumnDef;
import com.rtsapp.server.domain.support.jpa.TableDef;
import com.rtsapp.server.utils.ReflectionUtils;
import com.rtsapp.server.utils.StringUtils;

import javax.persistence.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by admin on 15-9-21.
 * JPA注解帮助类
 */
public class JpaUtils {

    private static final Map<String,String> JavaSqlTypes = new HashMap<String, String>( );

    static{
        ResultSet rs = null;

        JavaSqlTypes.put("boolean", "boolean");
        JavaSqlTypes.put("byte", "byte");
        JavaSqlTypes.put("short", "short");
        JavaSqlTypes.put("int", "int");
        JavaSqlTypes.put("long", "long");
        JavaSqlTypes.put("float", "float");
        JavaSqlTypes.put("double", "double");
        JavaSqlTypes.put("float", "float");


        JavaSqlTypes.put("java.lang.Boolean", "boolean");
        JavaSqlTypes.put("java.lang.Byte", "byte");
        JavaSqlTypes.put("java.lang.Short", "short");
        JavaSqlTypes.put("java.lang.Integer", "int");
        JavaSqlTypes.put("java.lang.Long", "long");
        JavaSqlTypes.put("java.lang.Float", "float");
        JavaSqlTypes.put("java.lang.Double", "double");
        JavaSqlTypes.put("java.lang.String", "string");

        JavaSqlTypes.put("byte[]", "Bytes");

        JavaSqlTypes.put( "java.util.Date", "Date" );

    }


    /**
     * @param entityClass
     * @return
     */
    public static  TableDef getTableDef(Class<?> entityClass){

        Annotation[] entityAnnotations  =  entityClass.getAnnotations();
        if( entityAnnotations == null || entityAnnotations.length < 1 ){
            throw new RuntimeException( "实体类没有任何注解" );
        }

        Annotation entityAnnotation = null ;
        Annotation tableAnnotation = null ;

        for( Annotation annotaion : entityAnnotations ){
            if(  annotaion.annotationType() == javax.persistence.Entity.class ){
                entityAnnotation = annotaion;
            }
            if( annotaion.annotationType() == javax.persistence.Table.class ){
                tableAnnotation = annotaion;
            }
        }
        if( entityAnnotation == null ){
            throw new RuntimeException( entityClass.getName() + " 没有实现Entity注解" );
        }

        String tableName = entityClass.getSimpleName();
        if( tableAnnotation != null ){

            Table tbl = (Table)tableAnnotation;

            if( tbl.name( ) != null && tbl.name().trim().length()  > 0 ){
                tableName = tbl.name();
            }

        }


        TableDef tableDef = new TableDef( tableName, entityClass.getSimpleName( ) );

        Field[] fields = entityClass.getDeclaredFields();
        Method[] methods = entityClass.getDeclaredMethods();
        boolean idInFields  = true;
        for( Method m : methods ){
            if( m.getAnnotation( javax.persistence.Id.class ) != null ) {
                idInFields = false;
                break;
            }
        }

        int joinColumnCount = 0;
        if( idInFields ){

            for( Field f : fields ){

                if( Modifier.isStatic( f.getModifiers() ) ){
                    continue;
                }


                if( f.getAnnotation( javax.persistence.Transient.class ) != null ){
                    continue;
                }

                if( f.getAnnotation( JoinColumn.class ) != null ){
                    joinColumnCount++;
                    if( joinColumnCount > 1 ){
                        throw new RuntimeException( "JoinColumn的数量不允许大于1" );
                    }
                }

                if( f.getAnnotation( javax.persistence.Id.class ) != null ){
                    ColumnDef columnDef = getColumnColumnDef(f);
                    tableDef.appendColumn( columnDef );
                    tableDef.setId( columnDef );
                }else if( f.getAnnotation( OneToMany.class ) != null  ){
                    continue;
                }else if( f.getAnnotation( ManyToOne.class ) != null  ){
                    tableDef.appendColumn( getColumnColumnDef(f) );
                }else if( f.getAnnotation( OneToOne.class ) != null  ){
                    if( f.getAnnotation( JoinColumn.class ) != null ){
                        tableDef.appendColumn( getColumnColumnDef(f) );
                    }else{
                        continue;
                    }
                }else{
                    tableDef.appendColumn( getColumnColumnDef(f) );
                }

            }

        }else{

            for( Method m : methods ){

                if( Modifier.isStatic( m.getModifiers() ) ){
                    continue;
                }

                if( m.getParameterCount() > 0 ){
                    continue;
                }

                if( ( m.getName().startsWith( "get" ) && m.getName().length() > 3 )
                        || (  m.getName().startsWith( "is" ) && m.getName().length() > 2 )
                        ){

                    if( m.getAnnotation( JoinColumn.class ) != null ){
                        joinColumnCount++;
                        if( joinColumnCount > 1 ){
                            throw new RuntimeException( "JoinColumn的数量不允许大于1" );
                        }
                    }

                    if( m.getAnnotation(javax.persistence.Transient.class) != null ){
                        continue;
                    }else if( m.getAnnotation( javax.persistence.Id.class ) != null ){

                        ColumnDef columnDef = getColumnColumnDef( m );
                        tableDef.appendColumn( columnDef );

                        tableDef.setId( columnDef );

                    }else if( m.getAnnotation( OneToMany.class ) != null  ){
                        continue;
                    }else if( m.getAnnotation( ManyToOne.class ) != null  ){
                        tableDef.appendColumn( getColumnColumnDef( m ) );
                    }else if( m.getAnnotation( OneToOne.class ) != null  ){
                        if( m.getAnnotation( JoinColumn.class) != null ) {
                            tableDef.appendColumn(getColumnColumnDef(m));
                        }else{
                            continue;
                        }
                    }else{
                        tableDef.appendColumn( getColumnColumnDef( m ) );
                    }

                }
            }

        }

        return tableDef;
    }

    public static ColumnDef getColumnColumnDef( Method m ){
        Annotation columnAnno = m.getAnnotation( Column.class );
        Annotation joinColumnAnno =  m.getAnnotation(JoinColumn.class);

        String filedName =  getFieldNameFromMethod(m);
        String fieldType = getTypeName(m.getReturnType());
        String columnName = filedName;

        if( columnAnno != null ){
            String annoName = (( Column)columnAnno).name();
            if( !StringUtils.isEmpty( annoName ) ){
                columnName = annoName;
            }

        }else if( joinColumnAnno != null ){
            String annoName = (( JoinColumn)joinColumnAnno).name( );
            if( !StringUtils.isEmpty( annoName ) ){
                columnName = annoName;
            }
        }

        ColumnDef columnDef = new ColumnDef( columnName , filedName, fieldType );

        if( joinColumnAnno != null ){
            TableDef joinTableDef  =  getTableDef(m.getReturnType());
            ColumnDef idColumn = joinTableDef.getId();
            columnDef.setJoinColumnInfo( true, idColumn.getFieldName(), idColumn.getFieldType() );
        }

        return columnDef;
    }

    private static String getFieldNameFromMethod( Method m ){



        int beginIndex = 3;
        if( m.getName().startsWith( "is" ) ){
            beginIndex = 2;
        }

        StringBuilder sb = new StringBuilder();
        sb.append(m.getName().substring( beginIndex ) );
        sb.setCharAt( 0, Character.toLowerCase( sb.charAt( 0 ) ) );
        return sb.toString();
    }


    private static String getTypeName( Class<?> clz ){
        return ReflectionUtils.getTypeName( clz );
    }


    public static ColumnDef getColumnColumnDef(Field f){
        Annotation columnAnno = f.getAnnotation(Column.class );
        Annotation joinColumnAnno = f.getAnnotation(JoinColumn.class );

        String columnName = f.getName();

        if( columnAnno != null ){
            columnName = (( Column)columnAnno).name();
        }else if( joinColumnAnno != null ){
            columnName = (( JoinColumn)joinColumnAnno).name( );
        }


        ColumnDef columnDef = new ColumnDef( columnName , f.getName(), getTypeName( f.getType() )  );
        if( joinColumnAnno != null ){
            TableDef joinTableDef  =  getTableDef( f.getType() );
            ColumnDef idColumn = joinTableDef.getId( );
            columnDef.setJoinColumnInfo( true, idColumn.getFieldName(), idColumn.getFieldType() );
        }

        return columnDef;
    }


    public static String getSqlMappingType( String javaType ){
        return JavaSqlTypes.get( javaType );
    }

}
